#include "clistenerimpl.h"
#include <iostream>
#include <string>
#include <cstring>
#include <arpa/inet.h>
#include <fstream>
#include <exception>



#define UNUSED(x) ((void)(x))

EnHandleResult CListenerImpl::OnPrepareConnect(ITcpClient *pSender, CONNID dwConnID, SOCKET socket)
{
    UNUSED(pSender);
    UNUSED(dwConnID);
    UNUSED(socket);
    return HR_OK;
}

EnHandleResult CListenerImpl::OnConnect(ITcpClient *pSender, CONNID dwConnID)
{
    char szAddress[100]{};
    int iAddressLen = sizeof(szAddress) / sizeof(char);
    u_int16_t usPort;

    pSender->GetRemoteHost(szAddress, iAddressLen, usPort);
    m_connected = true;
    if(m_conn_cb)
        m_conn_cb(szAddress,std::string(szAddress).length(),usPort,m_connected);
    std::cout<<"Conn Server IP: "<<std::string(szAddress)<<" Port: "<<usPort<<std::endl;
    return HR_OK;
}

EnHandleResult CListenerImpl::OnHandShake(ITcpClient *pSender, CONNID dwConnID)
{
    return HR_OK;
}

EnHandleResult CListenerImpl::OnReceive(ITcpClient *pSender, CONNID dwConnID, const BYTE *pData, int iLength)
{
    std::cout<<std::string((char*)pData,iLength)<<std::endl;
    UNUSED(pSender);
    UNUSED(dwConnID);
#if 0
    int remain = iLength-4;
    int data_size{0};
    while(remain >= data_size)
    {
        uint8_t header[4]{};
        memcpy(header,pData,4);
        data_size = ntohl(*(uint32_t*)header);
        if(data_size > 0)
        {
            remain -= data_size;
            if(m_msg_cb)
                m_msg_cb((char*)(pData+4),data_size);
        }
    }
#else
    if(m_msg_cb)
        m_msg_cb((char*)pData,iLength);
#endif
    return HR_OK;
}

EnHandleResult CListenerImpl::OnSend(ITcpClient *pSender, CONNID dwConnID, const BYTE *pData, int iLenght)
{
    return HR_OK;
}

EnHandleResult CListenerImpl::OnClose(ITcpClient *pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
{

    char buf[100]{};
    uint16_t port{};
    int addr_len{100};
    if(pSender)
        pSender->GetRemoteHost(buf,addr_len,port);
    m_connected = false;
    if(m_conn_cb)
        m_conn_cb(buf,std::string(buf).length(),port,m_connected);
    std::cout<<"Diss Server IP: "<<std::string(buf)<<" Port: "<<port<<std::endl;
    return HR_OK;
}
///////

EnHandleResult ServerListenerImpl::OnPrepareListen(ITcpServer *pServer, SOCKET socket)
{
    return HR_OK;
}

EnHandleResult ServerListenerImpl::OnAccept(ITcpServer *pServer, CONNID dwConnID, UINT_PTR soClient)
{
    if(pServer)
    {
        char buf[100]{};
        int ip_len=100;
        uint16_t port{};
        pServer->GetRemoteAddress(dwConnID,buf,ip_len,port);
        if(m_conn_cb)
            m_conn_cb(buf,ip_len,port,true);
        std::string ip{std::string(buf)};
        auto client_info = GetClientInfo(ip,port);

        {
            std::lock_guard<std::mutex>lok(m_mutex);
            m_conn_hash[client_info] = dwConnID;
        }
        return HR_OK;
    }
    return HR_ERROR;
}

EnHandleResult ServerListenerImpl::OnHandShake(ITcpServer *pServer, CONNID dwConnID)
{
    return HR_OK;
}

EnHandleResult ServerListenerImpl::OnReceive(ITcpServer *pServer, CONNID dwConnID, const BYTE *pData, int iLength)
{
#if 0
    if(pServer)
    {
        pServer->Send(dwConnID,pData,iLength);
        if(m_msg_cb)
            m_msg_cb((char*)pData,iLength);
        return HR_OK;

    }
    return HR_ERROR;
#else
    std::lock_guard<std::mutex>loc(m_conn_mutex);
    auto &ctx = m_conn_map[dwConnID];
    int offset{0};
    while(offset < iLength)
    {
        if(!ctx.header_done)
        {
            uint8_t header[4]{};
            memcpy(header,pData,4);
            ctx.total_size = ntohl(*(uint32_t*)header);
            if(ctx.total_size>0)
            {
                ctx.buffer.reserve(ctx.total_size);
                ctx.header_done = true;
                offset +=4;
            }
        }//if(!ctx.header_done)
        else
        {
            size_t  remain = ctx.total_size - ctx.received_size;
            size_t copy_len = std::min<size_t>(remain,iLength-offset);
            ctx.buffer.insert(ctx.buffer.end(),pData+offset,pData+offset+copy_len);
            ctx.received_size += copy_len;
            offset += copy_len;
            if(ctx.received_size == ctx.total_size)
            {
                try {
                    system("mkdir -p files/");
                    auto now = std::chrono::system_clock::now();
                    auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
                    std::string filename{"files/recv_" +std::to_string(timestamp)+".dat"};
                    std::ofstream outfile(filename,std::ios::binary | std::ios::trunc);
                    if(!outfile.is_open())
                    {
                        std::cerr<<"file open fail: "<<filename<<std::endl;
                        return HR_ERROR;
                    }
                    outfile.write((const char*)ctx.buffer.data(),ctx.buffer.size());
                    outfile.close();
                }  catch (std::exception &e) {
                    std::cerr<<"file oper fail :"<<e.what()<<std::endl;
                    return HR_ERROR;
                }
                //rest
                ctx.buffer.clear();
                ctx.header_done =false;
                ctx.received_size=0;
                ctx.total_size =0;
                break;
            }
        }
    }//while(offset < iLength)
    return HR_OK;
#endif
}

EnHandleResult ServerListenerImpl::OnSend(ITcpServer *pServer, CONNID dwConnID, const BYTE *pData, int iLength)
{
    return HR_OK;
}

EnHandleResult ServerListenerImpl::OnClose(ITcpServer *pServer, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
{
    char ip[40]{};
    int ip_len{40};
    uint16_t port{};
    pServer->GetRemoteAddress(dwConnID,ip,ip_len,port);
    if(m_conn_cb)
        m_conn_cb(ip,ip_len,port,false);
    auto temp_ip = std::string(ip);
    {
        std::lock_guard<std::mutex>loc(m_mutex);
        m_conn_hash.erase(GetClientInfo(temp_ip,port));
    }
    return HR_OK;
}

EnHandleResult ServerListenerImpl::OnShutdown(ITcpServer *pSender)
{
    std::cout<<"shutdown"<<std::endl;
    return HR_OK;
}



std::string ServerListenerImpl::GetClientInfo(std::string &ip, uint16_t port)
{
    return (ip+":"+std::to_string(port));
}
/////
/// \brief CClientPullListener::OnConnect
/// \param pSender
/// \param dwConnID
/// \return
///
EnHandleResult CClientPullListener::OnConnect(ITcpClient *pSender, CONNID dwConnID)
{
    char buf[40]{};
    int len{40};
    uint16_t port{};
    pSender->GetRemoteHost(buf,len,port);
    if(m_conn_cb)
        m_conn_cb(buf,std::string(buf).length(),port,true);
    return HR_OK;
}

EnHandleResult CClientPullListener::OnReceive(ITcpClient *pSender, CONNID dwConnID, int iLength)
{
    ITcpPullClient *pClient = ITcpPullClient::FromS(pSender);
    constexpr int required{8};
    int remain = iLength;
    while (remain >= required) {
        remain -= required;
        uint8_t buf[required]{};
        if(FR_OK == pClient->Fetch(buf,sizeof(buf)))
        {
            if(m_msg_cb)
                m_msg_cb((char*)(buf+4),4);
        }
    }

    return HR_OK;
}

EnHandleResult CClientPullListener::OnClose(ITcpClient *pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
{
    return HR_OK;
}
